﻿using Adnc.Application.Shared.Dtos;
using Adnc.Application.Shared.Services;
using Adnc.Infra.EventBus;
using Adnc.Infra.Helper;
using Adnc.Infra.IRepositories;
using Adnc.Infra.Repository.Entities.MongoEntities;
using Adnc.Shared.Events;
using Adnc.Shared.RpcServices.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using MoBaoPay;
using MoBaoPay.Model.Request;
using MoBaoPay.Model.Response;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using WYT.Pay.Application.Contracts.Dtos.Mobao;
using WYT.Pay.Application.Contracts.Dtos.Payment;
using WYT.Pay.Application.Contracts.Services;
using WYT.Pay.Repository.Entities;

namespace WYT.Pay.Application.Services
{
    /// <summary>
    /// 墨宝支付
    /// </summary>
    public class MoBaoAppService : AbstractAppService, IMoBaoAppService
    {
        private readonly IMoBaoPayClient _moBaoPayClient;

        private readonly IEfRepository<PaymentOrder> _orderPayRepository;

        private readonly IEfRepository<MobaoPayment> _mobaoPaymentRepository;

        private readonly IEfRepository<MoBaoCard> _mobaoCardRepository;

        private readonly IEfRepository<MobaoFundOut> _mobaoFunOutRepository;

        private readonly IMongoRepository<MessageLog> _messageLogRepository;

        private readonly IOptions<MoBaoOptions> _options;

        private readonly IConfiguration _config;

        private readonly IUserRpcService _userRpcService;

        private readonly IOrdersRpcService _ordersRpcService;

        private readonly IEventPublisher _eventPublisher;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="moBaoPayClient"></param>
        /// <param name="orderPayRepository"></param>
        /// <param name="config"></param>
        /// <param name="userRpcService"></param>
        /// <param name="ordersRpcService"></param>
        /// <param name="mobaoPaymentRepository"></param>
        /// <param name="mobaoFunOutRepository"></param>
        /// <param name="mobaoCardRepository"></param>
        /// <param name="options"></param>
        /// <param name="eventPublisher"></param>
        /// <param name="messageLogRepository"></param>
        public MoBaoAppService(IMoBaoPayClient moBaoPayClient
            ,IEfRepository<PaymentOrder> orderPayRepository
            ,IConfiguration config
            ,IUserRpcService userRpcService
            ,IOrdersRpcService ordersRpcService
            ,IEfRepository<MobaoPayment> mobaoPaymentRepository
            , IEfRepository<MobaoFundOut> mobaoFunOutRepository
            , IEfRepository<MoBaoCard> mobaoCardRepository
            , IOptions<MoBaoOptions> options
            , IEventPublisher eventPublisher
            , IMongoRepository<MessageLog> messageLogRepository)
        {
            _moBaoPayClient = moBaoPayClient;
            _orderPayRepository = orderPayRepository;
            _config = config;
            _userRpcService = userRpcService;
            _ordersRpcService = ordersRpcService;
            _mobaoPaymentRepository = mobaoPaymentRepository;
            _mobaoCardRepository = mobaoCardRepository;
            _mobaoFunOutRepository = mobaoFunOutRepository;
            _options = options;
            _eventPublisher = eventPublisher;
            _messageLogRepository = messageLogRepository;
        }

        /// <summary>
        /// 发起支付
        /// </summary>
        /// <param name="userid"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<AppSrvResult<WechatPayResponse>> GetWXPaymentInfor(long userid, SimpleInputDto<long> input)
        {
            var payinfor =  _orderPayRepository.Where(c => c.UserId == userid && c.OrderId == input.Value && c.State == 1).FirstOrDefault();
            if (payinfor == null)
                return Problem(HttpStatusCode.NotFound,"订单已支付或已过期");

            var userResponse = await _userRpcService.GetUserOpenId();
            if (!userResponse.IsSuccessStatusCode)
                return Problem(HttpStatusCode.InternalServerError, "服务错误");
            var openId = userResponse.Content;

            var data = new MoBaoWeChatPayInput
            {
                AppId = _config["WYT:WxOpenAppId"],
                GoodsDesc = "购买商品",
                GoodsName = "测试",
                MerchSyncUrl = _config["WYT:MerchSyncUrl"],
                MerchTradeNo = payinfor.OrderId.ToString(),
                MerchUrl = _config["WYT:MerchUrl"],
                OpenId = openId,
                TerminalId = "127.0.0.1",
                //TradeAmt = payinfor.Amount,
                TradeAmt = 0.01m,
                TradeTime = DateTime.Now.ToString("yyyyMMddHHmmss"),
                TradeSummer = "购买商品"
            };

            var mobaoResponse = await _moBaoPayClient.ExecuteWeChatAsync(data, _options.Value);

            if (mobaoResponse.RetCode != "1" || mobaoResponse.RetData == null || string.IsNullOrEmpty(mobaoResponse.RetData.CodeUrl))
                return Problem(HttpStatusCode.InternalServerError, "微信支付失败！");

            var weChat = JsonConvert.DeserializeObject<WechatPayResponse>(mobaoResponse.RetData.CodeUrl, new JsonSerializerSettings
            {
                ContractResolver = new CamelCasePropertyNamesContractResolver()
            });

           return weChat;
        }

        /// <summary>
        /// 支付回调
        /// </summary>
        /// <param name="verify"></param>
        /// <returns></returns>
        public async Task<AppSrvResult<bool>> NotifyVerify(MoBaoNotifyInput verify)
        {
            await _messageLogRepository.AddAsync(new MessageLog { LogType = "支付回调", Message = JsonConvert.SerializeObject(verify), Describe = "测试", Time = DateTime.Now });

            //校验数据
            var result = verify.Verify("mobao.cer");
            if (result == false)
                return false;

            await _mobaoPaymentRepository.InsertAsync(new MobaoPayment {
                CustNo = verify.CustNo,
                Id = IdGenerater.GetNextId(),
                MerchParam = verify.MerchParam,
                MerchUrl = verify.MerchUrl,
                SignMsg = verify.SignMsg,
                OrderNo = verify.OrderNo,
                TradeAmt = verify.TradeAmt,
                TradeTime =  verify.TradeTime,
                NotifyTime = verify.NotifyTime,
                OrderStatus = verify.OrderStatus,
                MerchTradeNo = verify.MerchTradeNo
            });

            var orderId = long.Parse(verify.MerchTradeNo);
            var payinfor = _orderPayRepository.Where(c => c.OrderId == orderId).FirstOrDefault();
            if (payinfor != null)
            {
                payinfor.State = 2;
                payinfor.PayNo = verify.OrderNo;
                payinfor.TradeTime = DateTime.ParseExact(verify.TradeTime, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);

                //修改订单状态
                var changeEventId = IdGenerater.GetNextId();
                var changeEventData = new OrderStateChangeEvent.EventData() { OrderId = payinfor.OrderId, ChangeTime = payinfor.TradeTime.Value, ChangeType = 1 };
                var changeEventSource = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName;
                await _eventPublisher.PublishAsync(new OrderStateChangeEvent(changeEventId, changeEventData, changeEventSource));

                await _orderPayRepository.UpdateAsync(payinfor, UpdatingProps<PaymentOrder>(c => c.State,c => c.PayNo));
            }
            return true;
        }

        /// <summary>
        /// 用户提现到银行卡
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<bool> UserWithdrawal(WithdrawalDto input)
        {
            var bank = await _mobaoCardRepository.FindAsync(input.BankCardId);
            if (bank == null || bank.State != 1)
                return false;
            var data = new MoBaoFundOutInput
            {
                AccInName = bank.CardName,
                AccInNo = bank.CardNo,
                MerchTradeNo = input.ApplicationID.ToString(),
                MerchUrl = "123",
                TerminalId = "127.0.0.1",
                TradeAmt = input.TradeAmt,
                TradeSummer = "用户提现"
            };
            var response =  await _moBaoPayClient.ExecuteFundOutAsync(data, _options.Value);
            if (response.RetCode != "1" || response.RetData == null)
                return false;
            else
            {
                var redata = response.RetData;
                await _mobaoFunOutRepository.InsertAsync(new MobaoFundOut { 
                    Id = IdGenerater.GetNextId(),
                    UserId = input.UserId,
                    AccInName =bank.CardName,
                    AccInNo = bank.CardNo,
                    Amount = redata.TradeAmt,
                    MerchTradeNo = redata.MerchTradeNo,
                    OrderOutNo = redata.OrderOutNo,
                    TradeSummer = redata.TradeSummer,
                    TradeTime = DateTime.Now,
                    Type = 1,
                    OrderStatus = redata.OrderStatus,
                });

                return true;
            }
        }

        #region 银行卡

        /// <summary>
        /// 绑定银行卡
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<AppSrvResult<Boolean>> BindBankCard(long userId, BindCardDto input)
        {
            if (await _mobaoCardRepository.AnyAsync(c => c.CardNo == input.CardNo && c.State == 1))
            {
                return Problem(HttpStatusCode.BadRequest, "此卡已被绑定，请更换银行卡！");
            }

            
            var moBaoInput =  new MoBaoBindCardInput { 
                CardName = input.CardName,
                CardNo = input.CardNo,
                IdCard = input.IdCard,
                PhoneNo = input.PhoneNo,
                CustName = input.CustName,
            };

            var response = await _moBaoPayClient.ExecuteBindCardAsync(moBaoInput, _options.Value);

            if (response.RetCode  != "1" || response.RetData.ValidateStatus == 2)
                return Problem(HttpStatusCode.BadRequest,response.RetMsg);

            MoBaoCard card = new MoBaoCard
            {
                Id = IdGenerater.GetNextId(),
                UserId = userId,
                BankName = response.RetData.BankName,
                BankCode = response.RetData.BankCode,
                CardName = response.RetData.CardName,
                CardNo = response.RetData.CardNo,
                CustNo = response.RetData.CardNo,
                IdCard = response.RetData.IdCard,
                PhoneNo = response.RetData.PhoneNo,
                CustName = input.CustName,
                State = 1,
                CreateTime = DateTime.Now,
                IsDefault = false
            };
            var defaultCard = _mobaoCardRepository.Where(c => c.UserId == userId && c.IsDefault).FirstOrDefault();
            if (defaultCard == null)
                card.IsDefault = true;
            else if (input.IsDefault)
            {
                defaultCard.IsDefault = false;
                card.IsDefault = true;
                await _mobaoCardRepository.UpdateAsync(defaultCard,UpdatingProps<MoBaoCard>(c => c.IsDefault));
            }

            await _mobaoCardRepository.InsertAsync(card);

            return AppSrvResult(true);
        }

        /// <summary>
        /// 银行卡解绑
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="Id"></param>
        /// <returns></returns>
        public async Task<AppSrvResult> UnbindBankCard(long userId, long Id)
        {
            var card = await _mobaoCardRepository.FindAsync(Id);
            if (card == null || card.UserId != userId)
                return Problem(null, "");
            card.State = -1;
            await _mobaoCardRepository.UpdateAsync(card,UpdatingProps<MoBaoCard>(x => x.State));
            return AppSrvResult();
        }


        /// <summary>
        /// 获取银行卡列表
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public async Task<AppSrvResult<List<BankCardDto>>> GetBankCarList(long userId)
        {
            return await _mobaoCardRepository.Where(c => c.UserId == userId && c.State == 1)
                .OrderBy(c => c.IsDefault)
                .Select(c => Mapper.Map<BankCardDto>(c))
                .ToListAsync();
        }

        /// <summary>
        /// 设置默认银行卡
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="Id"></param>
        /// <returns></returns>
        public async Task<AppSrvResult<Boolean>> DefaultBankCardAsync(long userId, long Id)
        {
            var defualt = await _mobaoCardRepository.FindAsync(c => c.UserId.Equals(userId) && c.IsDefault == true && c.State == 1);
            if (defualt.Id == Id)
                return true;
            var newDefualt = await _mobaoCardRepository.FindAsync(Id);
            if (newDefualt == null || newDefualt.State != 1)
                return Problem(HttpStatusCode.BadRequest, "参数错误");

            var propertyNameAndValues = new Dictionary<long, List<(string propertyName, dynamic propertyValue)>>
            {
                {
                    newDefualt.Id,
                    new List<(string Column, dynamic Value)>
                    {
                       ("IsDefault",true),
                    }
                },
                {
                    defualt.Id,
                    new List<(string Column, dynamic Value)>
                    {
                       ("IsDefault",false),
                    }
                }
            };

            await _mobaoCardRepository.UpdateRangeAsync(propertyNameAndValues);

            return true;
        }


        /// <summary>
        /// 获取用户默认银行卡号
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public async Task<AppSrvResult<BankCardDto>> GetDefaultBankCard(long userId)
        {
            var data = await _mobaoCardRepository.FindAsync(c => c.UserId == userId && c.IsDefault && c.State == 1 );
            if(data != null)
                return Mapper.Map<BankCardDto>(data);
            else 
                return AppSrvResult();
        }

        /// <summary>
        /// 根据ID获取银行卡信息
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<AppSrvResult<BankCardDto>> GetBankCardinfor(long id)
        {
            var data = await _mobaoCardRepository.FindAsync(id);
            if (data == null)
                return Problem(HttpStatusCode.NotFound);
            return Mapper.Map<BankCardDto>(data);
        }

        #endregion
    }
}
